package net.omnivention.proxy;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import org.apache.log4j.Logger;

public class HttpUtility {
	private static Logger logger = Logger.getLogger(HttpUtility.class);

	public static String NEWLINE = "\r\n";

	private static HttpHeaders parseHttpHeader(InputStream input) {
		HttpHeaders header = new HttpHeaders();
		String line;
		try {
			while ((line = IOUtility.readLine(input)) != null) {
				logger.debug(line);
				header.add(line);
				if (line.length() == 0 || line.length() == 1) {
					break;
				}
			}
		} catch (IOException e) {
			logger.error(e + "\n" + header);
			e.printStackTrace();
		}

		return header;
	}

	public static HttpRequest parseRequest(InputStream input) {
		HttpRequest request = new HttpRequest();
		try {
			String line = IOUtility.readLine(input);
			request.injectRequestLine(line);

			HttpHeaders header = parseHttpHeader(input);
			request.setHeader(header);
			request.setHost(header.getHeaderValue("Host"));
			
			if (request.getMethod().equals("POST")) {
				HttpBody body = parseBody(header, input);
				request.setBody(body);
			} else {
				request.setBody(new HttpBody());
			}
		} catch (IOException e) {
			logger.error(e);
			e.printStackTrace();
		}
		return request;
	}

	private static HttpBody parseBody(HttpHeaders header, InputStream input) throws IOException {
		HttpBody hbody = new HttpBody();

		String contentLength = header.getHeaderValue("Content-Length");
		if (contentLength != null) {
			int cl = Integer.parseInt(contentLength);

			byte[] body = new byte[cl];
			int bodySize = 0;

			while (bodySize < cl) {
				bodySize += input.read(body, bodySize, cl - bodySize);
			}

			logger.info("content-length: " + contentLength);
			logger.info("bodySize=" + bodySize);
			hbody.setContent(body);
			return hbody;
		}

		String connection = header.getHeaderValue("Connection");
		logger.debug("connection: " + connection);
		if ("close".equalsIgnoreCase(connection)) {
			// No content-length header so read until end of stream
			ArrayList<byte[]> blocks = new ArrayList<byte[]>();
			int bodySize = 0;
			while (true) {
				int avail = input.available();
				if (avail > 0) {
					byte[] block = new byte[avail];
					int r = input.read(block);
					if (r != -1) {
						bodySize += r;
						blocks.add(block);
					}

				} else if (avail == 0) {
					input.mark(1);
					int r = input.read();
					if (r == -1) {
						logger.info("Body EOF. bodySize=" + bodySize + " blocks.size()=" + blocks.size());
						byte[] body = new byte[bodySize];
						int contentIndex = 0;
						for (int i = 0; i < blocks.size(); i++) {
							byte[] b = blocks.get(i);
							for (int j = 0; j < b.length; j++) {
								body[contentIndex++] = b[j];
							}
						}
						hbody.setContent(body);
						return hbody;
					} else {
						input.reset();
					}
				}
			}
		}

		return hbody;
	}

	public static HttpResponse parseResponse(InputStream input) {
		HttpResponse response = new HttpResponse();
		try {
			String line = IOUtility.readLine(input);
			response.injectStatusLine(line);
			HttpHeaders header = parseHttpHeader(input);
			response.setHeaders(header);

			HttpBody body = parseBody(header, input);

			response.setBody(body);
		} catch (IOException ex) {
			logger.error(ex);
			ex.printStackTrace();
		}

		return response;
	}

}
